package com.scnu.edu.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.scnu.edu.dto.MovieDTO;
import com.scnu.edu.entity.*;
import com.scnu.edu.mapper.*;
import com.scnu.edu.service.MovieService;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class MovieServiceImpl extends ServiceImpl<MovieMapper, Movie> implements MovieService {
    @Resource
    private CategoryMapper categoryMapper;

    @Resource
    private RegionMapper regionMapper;
@Resource
private MovieActorMapper movieActorMapper;
    @Override
    public List<Category> getAllCategories() {
        return categoryMapper.selectList(null);
    }

    @Override
    public List<Region> getAllRegions() {
        return regionMapper.selectList(null);
    }

    @Override
    public Map<String, Object> advancedSearch(String keyword, Long categoryId, Long regionId, String creator,String sort,
            Integer pageNo, Integer pageSize) {
        // 参数校验
        if (pageNo == null || pageNo < 1) {
            pageNo = 1;
        }
        if (pageSize == null || pageSize < 1) {
            pageSize = 10;
        }

        LambdaQueryWrapper<Movie> wrapper = new LambdaQueryWrapper<>();

        // 关键词搜索：电影名或简介
        if (keyword != null && !keyword.isEmpty()) {
            wrapper.and(q -> q.like(Movie::getName, keyword)
                    .or()
                    .like(Movie::getDescription, keyword));
        }

        // 分类筛选：使用exists子查询提高效率
        if (categoryId != null) {
            wrapper.exists("SELECT 1 FROM movie_category_relation WHERE movie_id = movie.id AND category_id = {0}", 
                    categoryId);
        }

        // 地区筛选
        if (regionId != null) {
            wrapper.eq(Movie::getRegionId, regionId);
        }

        // 参演人员筛选
        if (creator != null && !creator.isEmpty()) {
            // 查询匹配的导演ID列表
            List<Long> directorIds = directorMapper.selectList(new LambdaQueryWrapper<Director>()
                    .like(Director::getName, creator))
                .stream()
                .map(Director::getId)
                .collect(Collectors.toList());

            // 查询匹配的演员ID列表
            List<Long> actorIds = actorMapper.selectList(new LambdaQueryWrapper<Actor>()
                    .like(Actor::getName, creator))
                .stream()
                .map(Actor::getId)
                .collect(Collectors.toList());

            // 构建关联查询条件
            wrapper.and(w -> w
                .inSql(Movie::getId, 
                    "SELECT movie_id FROM movie_director WHERE director_id IN (" + 
                    directorIds.stream().map(String::valueOf).collect(Collectors.joining(",")) + ")")
                .or()
                .inSql(Movie::getId,
                    "SELECT movie_id FROM movie_actor WHERE actor_id IN (" +
                    actorIds.stream().map(String::valueOf).collect(Collectors.joining(",")) + ")")
            );
        }

        // 排序处理
        if (sort != null) {
            switch (sort) {
                case "play_count":
                    wrapper.orderByDesc(Movie::getPlayCount);
                    break;
                case "score":
                    wrapper.orderByDesc(Movie::getScore);
                    break;
                case "release_date":
                    wrapper.orderByDesc(Movie::getReleaseDate);
                    break;
                default:
                    wrapper.orderByDesc(Movie::getReleaseDate);
            }
        } else {
            wrapper.orderByDesc(Movie::getReleaseDate);
        }

        // 执行分页查询
        IPage<Movie> page = new Page<>(pageNo, pageSize);
        page = this.page(page, wrapper);

        // 构建返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("records", page.getRecords().stream()
                .map(this::convertToMovieDTO)
                .collect(Collectors.toList()));
        result.put("total", page.getTotal());
        result.put("pageCount", page.getPages());
        return result;
    }

    private MovieDTO convertToMovieDTO(Movie movie) {
        return new MovieDTO(movie);
    }

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    private static final String WEEKLY_RANKING_KEY = "movie:ranking:weekly";
    private static final String MONTHLY_RANKING_KEY = "movie:ranking:monthly";
    private static final String ALL_TIME_RANKING_KEY = "movie:ranking:all";
    private static final String RATING_RANKING_KEY = "movie:ranking:rating";

    @Override
    public Map<String, Object> queryPage(String title, String category, Integer pageNo, Integer pageSize) {
        LambdaQueryWrapper<Movie> wrapper = new LambdaQueryWrapper<>();
        if (title != null && !title.isEmpty()) {
            wrapper.like(Movie::getName, title);
        }
        if (category != null && !category.isEmpty()) {
            wrapper.eq(Movie::getCategoryId, category);
        }
        wrapper.orderByDesc(Movie::getReleaseDate);

        IPage<Movie> page = new Page<>(pageNo, pageSize);
        page = this.page(page, wrapper);

        Map<String, Object> result = new HashMap<>();
        result.put("records", page.getRecords().stream()
                .map(this::convertToMovieDTO)
                .collect(Collectors.toList()));
        result.put("total", page.getTotal());
        result.put("pageCount", page.getPages());
        return result;
    }

    @Override
    public List<MovieDTO> getWeeklyRanking() {
        Set<ZSetOperations.TypedTuple<Object>> tuples = redisTemplate.opsForZSet()
                .reverseRangeWithScores(WEEKLY_RANKING_KEY, 0, 9);
        if (tuples == null || tuples.isEmpty()) {
            return getDefaultRanking().stream()
                    .map(this::convertToMovieDTO)
                    .collect(Collectors.toList());
        }
        return tuples.stream()
                .map(tuple -> {
                    Integer movieId = (Integer) tuple.getValue();
                    return getMovieDTOById(movieId.longValue());
                })
                .collect(Collectors.toList());
    }

    @Override
    public List<MovieDTO> getMonthlyRanking() {
        Set<ZSetOperations.TypedTuple<Object>> tuples = redisTemplate.opsForZSet()
                .reverseRangeWithScores(MONTHLY_RANKING_KEY, 0, 9);
        if (tuples == null || tuples.isEmpty()) {
            return getDefaultRanking().stream()
                    .map(this::convertToMovieDTO)
                    .collect(Collectors.toList());
        }
        return tuples.stream()
                .map(tuple -> {
                    Integer movieId = (Integer) tuple.getValue();
                    return getMovieDTOById(movieId.longValue());
                })
                .collect(Collectors.toList());
    }

    @Override
    public List<MovieDTO> getAllTimeRanking() {
        LambdaQueryWrapper<Movie> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(Movie::getPlayCount)
                .last("limit 10");
        return this.list(wrapper).stream()
                .map(this::convertToMovieDTO)
                .collect(Collectors.toList());
    }

    @Override
    public List<MovieDTO> getRatingRanking() {
        LambdaQueryWrapper<Movie> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(Movie::getScore)
                .last("limit 10");
        return this.list(wrapper).stream()
                .map(this::convertToMovieDTO)
                .collect(Collectors.toList());
    }

    @Override
    public Map<String, Object> getDirectorMovies(Integer directorId, Integer pageNo, Integer pageSize) {
        LambdaQueryWrapper<Movie> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(Movie::getId,
                new LambdaQueryWrapper<MovieDirector>()
                        .select(MovieDirector::getMovieId)
                        .eq(MovieDirector::getDirectorId, directorId))
                .orderByDesc(Movie::getReleaseDate);

        IPage<Movie> page = new Page<>(pageNo, pageSize);
        page = this.page(page, wrapper);

        Map<String, Object> result = new HashMap<>();
        result.put("records", page.getRecords());
        result.put("total", page.getTotal());
        result.put("pageCount", page.getPages());
        return result;
    }

    @Override
    public Map<String, Object> getActorMovies(Integer actorId, Integer pageNo, Integer pageSize) {
        // 创建查询条件
        LambdaQueryWrapper<Movie> wrapper = new LambdaQueryWrapper<>();
        wrapper.inSql(Movie::getId,
                        "SELECT movie_id FROM movie_actor WHERE actor_id = " + actorId)
                .orderByDesc(Movie::getReleaseDate)
                .last("LIMIT 4");

        // 执行查询
        List<Movie> movieList = this.list(wrapper);

        // 构造返回的 Map
        Map<String, Object> result = new HashMap<>();
        result.put("movies", movieList); // 电影列表

        return result;
    }

    @Override
    public void incrementPlayCount(Integer movieId) {
        // 更新数据库播放次数
        Movie movie = this.getById(movieId);
        if (movie != null) {
            movie.setPlayCount(movie.getPlayCount() + 1);
            this.updateById(movie);
        }

        // 更新Redis排行榜
        LocalDateTime now = LocalDateTime.now();
        LocalDate weekStart = now.toLocalDate().minusDays(now.getDayOfWeek().getValue() - 1);
        LocalDate monthStart = now.toLocalDate().withDayOfMonth(1);

        // 转换为时间戳
        long weekStartTime = weekStart.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
        long monthStartTime = monthStart.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();

        // 清理过期的数据
        redisTemplate.opsForZSet().removeRangeByScore(WEEKLY_RANKING_KEY, 0, weekStartTime - 1);
        redisTemplate.opsForZSet().removeRangeByScore(MONTHLY_RANKING_KEY, 0, monthStartTime - 1);

        // 更新排行榜
        redisTemplate.opsForZSet().incrementScore(WEEKLY_RANKING_KEY, movieId, 1);
        redisTemplate.opsForZSet().incrementScore(MONTHLY_RANKING_KEY, movieId, 1);
        redisTemplate.opsForZSet().incrementScore(ALL_TIME_RANKING_KEY, movieId, 1);
    }

    @Override
    public List<Movie> findMoviesByActorId(Long actorId) {
        // 先通过关系表查询电影ID列表
        List<Long> movieIds = movieActorMapper.selectList(
                new LambdaQueryWrapper<MovieActor>()
                        .select(MovieActor::getMovieId)
                        .eq(MovieActor::getActorId, actorId))
                .stream()
                .map(MovieActor::getMovieId)
                .collect(Collectors.toList());

        if (movieIds.isEmpty()) {
            return Collections.emptyList();
        }

        // 再通过电影ID列表查询电影详情
        return this.list(new LambdaQueryWrapper<Movie>()
                .in(Movie::getId, movieIds)
                .orderByDesc(Movie::getReleaseDate)
                .last("limit 4"));
    }

    @Override
    public List<Movie> findMoviesByDirectorId(Long directorId) {
        // 先通过关系表查询电影ID列表
        List<Long> movieIds = movieDirectorMapper.selectList(
                new LambdaQueryWrapper<MovieDirector>()
                        .select(MovieDirector::getMovieId)
                        .eq(MovieDirector::getDirectorId, directorId))
                .stream()
                .map(MovieDirector::getMovieId)
                .collect(Collectors.toList());

        if (movieIds.isEmpty()) {
            return Collections.emptyList();
        }

        // 再通过电影ID列表查询电影详情
        return this.list(new LambdaQueryWrapper<Movie>()
                .in(Movie::getId, movieIds)
                .orderByDesc(Movie::getReleaseDate)
                .last("limit 4"));
    }

    @Override
    public Map<String, Object> search(String keyword, Integer pageNo, Integer pageSize) {
        LambdaQueryWrapper<Movie> wrapper = new LambdaQueryWrapper<>();
        if (keyword != null && !keyword.isEmpty()) {
            wrapper.like(Movie::getName, keyword)
                    .or()
                    .like(Movie::getDescription, keyword);
        }
        wrapper.orderByDesc(Movie::getReleaseDate);

        IPage<Movie> page = new Page<>(pageNo, pageSize);
        page = this.page(page, wrapper);

        Map<String, Object> result = new HashMap<>();
        result.put("records", page.getRecords());
        result.put("total", page.getTotal());
        result.put("pageCount", page.getPages());
        return result;
    }

    @Resource
    private DirectorMapper directorMapper;

    @Resource
    private ActorMapper actorMapper;

    @Resource
    private MovieCategoryRelationMapper movieCategoryRelationMapper;

    @Resource
    private MovieDirectorMapper movieDirectorMapper;

    @Override
    public List<Category> findCategories() {
        return categoryMapper.selectList(null);
    }

    @Override
    public MovieDTO getMovieWithRelations(Long movieId) {
        MovieDTO dto = new MovieDTO(movieId);

        return dto;
    }

    @Override
    public MovieDTO getMovieDTOById(Long movieId) {
        MovieDTO dto = new MovieDTO(movieId);

        return dto;
    }

    @Override
    public Map<String, List<MovieDTO>> getSidebarRankings() {
        Map<String, List<MovieDTO>> rankings = new HashMap<>();
        rankings.put("weekly", getWeeklyRanking());
        rankings.put("monthly", getMonthlyRanking());
        rankings.put("allTime", getAllTimeRanking());
        rankings.put("rating", getRatingRanking());
        return rankings;
    }

    private List<Movie> getDefaultRanking() {
        LambdaQueryWrapper<Movie> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(Movie::getPlayCount)
                .last("limit 10");
        return this.list(wrapper);
    }

    @Override
    public void updateMovieCategories(Long movieId, List<Long> categoryIds) {
        // 先删除现有的关联
        LambdaQueryWrapper<MovieCategoryRelation> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MovieCategoryRelation::getMovieId, movieId);
        movieCategoryRelationMapper.delete(wrapper);

        // 添加新的关联
        if (categoryIds != null && !categoryIds.isEmpty()) {
            for (Long categoryId : categoryIds) {
                MovieCategoryRelation relation = new MovieCategoryRelation();
                relation.setMovieId(movieId);
                relation.setCategoryId(categoryId);
                movieCategoryRelationMapper.insert(relation);
            }
        }
    }

    @Override
    public List<Long> getCategoryIdsByMovieId(Long movieId) {
        LambdaQueryWrapper<MovieCategoryRelation> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MovieCategoryRelation::getMovieId, movieId);
        return movieCategoryRelationMapper.selectList(wrapper)
                .stream()
                .map(MovieCategoryRelation::getCategoryId)
                .collect(Collectors.toList());
    }

    @Override
    public List<Movie> getRecommendationsByActor(Long actorId) {
        // 获取该演员的所有电影
        List<Movie> actorMovies = findMoviesByActorId(actorId);
        if (actorMovies.isEmpty()) {
            return getRatingRanking().stream()
                    .map(dto -> this.getById(dto.getId()))
                    .limit(5)
                    .collect(Collectors.toList());
        }

        // 获取这些电影的类型
        Set<Long> categoryIds = actorMovies.stream()
                .map(Movie::getId)
                .flatMap(movieId -> getCategoryIdsByMovieId(movieId).stream())
                .collect(Collectors.toSet());

        // 查找相同类型的高分电影
        LambdaQueryWrapper<Movie> wrapper = new LambdaQueryWrapper<>();
        wrapper.exists(
                "SELECT 1 FROM movie_category_relation mcr WHERE mcr.movie_id = movie.id AND mcr.category_id IN (" +
                        String.join(",", categoryIds.stream().map(String::valueOf).collect(Collectors.toList())) + ")")
                .notIn(Movie::getId, actorMovies.stream().map(Movie::getId).collect(Collectors.toList()))
                .orderByDesc(Movie::getScore)
                .last("limit 5");

        return this.list(wrapper);
    }

    @Override
    public List<Movie> getRecommendationsByDirector(Long directorId) {
        // 获取该导演的所有电影
        List<Movie> directorMovies = findMoviesByDirectorId(directorId);
        if (directorMovies.isEmpty()) {
            return getRatingRanking().stream()
                    .map(dto -> this.getById(dto.getId()))
                    .limit(5)
                    .collect(Collectors.toList());
        }

        // 获取这些电影的类型
        Set<Long> categoryIds = directorMovies.stream()
                .map(Movie::getId)
                .flatMap(movieId -> getCategoryIdsByMovieId(movieId).stream())
                .collect(Collectors.toSet());

        // 查找相同类型的高分电影
        LambdaQueryWrapper<Movie> wrapper = new LambdaQueryWrapper<>();
        wrapper.exists(
                "SELECT 1 FROM movie_category_relation mcr WHERE mcr.movie_id = movie.id AND mcr.category_id IN (" +
                        String.join(",", categoryIds.stream().map(String::valueOf).collect(Collectors.toList())) + ")")
                .notIn(Movie::getId, directorMovies.stream().map(Movie::getId).collect(Collectors.toList()))
                .orderByDesc(Movie::getScore)
                .last("limit 5");

        return this.list(wrapper);
    }
}